package com.brillilab.service.logic.method;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.brillilab.common.alioss.OssFileEnum;
import com.brillilab.common.constant.ResultEnum;
import com.brillilab.common.entity.PageVo;
import com.brillilab.common.exceptions.BrillilabException;
import com.brillilab.common.redis.RedisCache;
import com.brillilab.domain.enums.common.BoolEnum;
import com.brillilab.domain.enums.lab.LabMemberPowerEnum.Power;
import com.brillilab.domain.enums.method.MethodDealStateEnum;
import com.brillilab.domain.enums.method.MethodSourceEnum;
import com.brillilab.domain.enums.method.RecommendAuditStateEnum;
import com.brillilab.domain.po.lab.Lab;
import com.brillilab.domain.po.method.Method;
import com.brillilab.domain.po.method.MethodFullInfo;
import com.brillilab.domain.po.method.MethodPhase;
import com.brillilab.domain.po.method.MethodRecommend;
import com.brillilab.domain.po.method.MethodRecommendAudit;
import com.brillilab.domain.po.method.MethodRecommendReceive;
import com.brillilab.domain.po.method.MethodRelateReagent;
import com.brillilab.domain.po.method.MethodRelateTimer;
import com.brillilab.domain.po.method.MethodStep;
import com.brillilab.domain.po.method.MethodType;
import com.brillilab.domain.po.method.RecommendMethodSync;
import com.brillilab.domain.po.system.FileManage;
import com.brillilab.domain.vo.lab.LabMemberVo;
import com.brillilab.domain.vo.method.CreateMethodRecommend;
import com.brillilab.domain.vo.method.MethodDetail;
import com.brillilab.domain.vo.method.MethodRecommendAuditResult;
import com.brillilab.domain.vo.method.MethodRecommendDeal;
import com.brillilab.domain.vo.method.MethodRecommendInfo;
import com.brillilab.domain.vo.method.MethodRecommendListItem;
import com.brillilab.domain.vo.method.MethodSaveDto;
import com.brillilab.domain.vo.method.NewMethod;
import com.brillilab.domain.vo.user.UserInfoVo;
import com.brillilab.domain.vo.user.UsersVo;
import com.brillilab.service.core.lab.ILabMemberService;
import com.brillilab.service.core.lab.ILabService;
import com.brillilab.service.core.method.IMethodPhaseService;
import com.brillilab.service.core.method.IMethodRecommendAuditService;
import com.brillilab.service.core.method.IMethodRecommendReceiveService;
import com.brillilab.service.core.method.IMethodRecommendService;
import com.brillilab.service.core.method.IMethodRelateReagentService;
import com.brillilab.service.core.method.IMethodRelateTimerService;
import com.brillilab.service.core.method.IMethodService;
import com.brillilab.service.core.method.IMethodStepService;
import com.brillilab.service.core.method.IMethodTypeService;
import com.brillilab.service.core.method.IRecommendMethodSyncService;
import com.brillilab.service.core.system.IFileManageService;
import com.brillilab.service.module.message.producer.MessageProducer;
import com.brillilab.service.module.message.template.impl.method.ReceiveMemberRecommendMethodTemp;

import lombok.extern.slf4j.Slf4j;

/**
 * @author wuzhiyu
 * @Title: MethodRecommendLogic
 * @ProjectName brill_lab
 * @Description: TODO
 * @date 2019/2/2216:08
 */
@Service
@Transactional
@Slf4j
public class MethodRecommendLogic {

	@Resource
	private IMethodService methodService;
	@Resource
	private IMethodTypeService methodTypeService;
	@Resource
	private IRecommendMethodSyncService recommendMethodSyncService;
	@Resource
	private IMethodRecommendAuditService methodRecommendAuditService;
	@Resource
	private IMethodRecommendService methodRecommendService;
	@Resource
	private IMethodRecommendReceiveService methodRecommendReceive;
	@Resource
	private MethodLogic methodLogic;
	@Resource
	private ILabService labService;
	@Resource
	private IMethodPhaseService methodPhaseService;
	@Resource
	private IMethodStepService methodStepService;
	@Resource
	private ILabMemberService labMemberService;
	@Resource
	private IMethodRelateReagentService methodRelateReagentService;
	@Resource
	private IMethodRelateTimerService methodRelateTimerService;
	@Resource
	private MessageProducer messageProducer;
	@Resource
	private IFileManageService fileManageService;
	@Resource
	private RedisCache redisCache;

	/**
	 * @return boolean
	 * @Description: 提交推荐方法去审核
	 * @Params: [recommend, user]
	 * @author wuzhiyu
	 * @date 2019/2/26 17:21
	 */
	public boolean createRecommendAudit(CreateMethodRecommend recommend, UserInfoVo user) {
		List<MethodRecommendAudit> list = methodRecommendAuditService.selectByMethodIds(user.getLabId(),
				recommend.getMethodIdList());
		Assert.isTrue(CollectionUtils.isEmpty(list), "方法不能重复推荐");

		List<Method> methodList = methodService.listByIdList(recommend.getMethodIdList(), user.getId());
		Assert.isTrue(!CollectionUtils.isEmpty(methodList), "数据不存在");
		List<MethodRecommendAudit> saveList = new ArrayList<>();
		List<MethodType> typeList = methodTypeService.listUserAllType(user.getId());
		methodList.forEach(method -> {
			MethodType methodType = typeList.stream().filter(t -> t.getId().equals(method.getMethodTypeId()))
					.findFirst().get();
			MethodRecommendAudit audit = new MethodRecommendAudit();
			audit.setLabId(user.getLabId());
			audit.setMethodId(method.getId());
			audit.setReason(recommend.getReason());
			audit.setRecommenderId(user.getLabMemberId());
			audit.setState(RecommendAuditStateEnum.AuditWait.getState());
			audit.setFirstMethodTypeId(methodType.getFirstId());
			audit.setMethodTypeId(methodType.getId());
			audit.setSecondMethodTypeId(methodType.getSecondId());
			saveList.add(audit);
		});
		boolean b = methodRecommendAuditService.saveList(saveList);
		if (b) {
			List<UsersVo> usersVoList = labMemberService.selectMemberByPower(user.getLabId(),
					Arrays.asList(Power.METHOD.getValue()));
			saveList.forEach(m -> {
				// 获取推荐方法信息
				Method recommendMethod = methodService.getById(m.getMethodId());
				// 获取推荐审核ID，即：业务关联referId
				MethodRecommendAudit recommendAuditInfo = methodRecommendAuditService
						.getRecommendAuditByMethodId(m.getMethodId(), user.getLabId());
				receiveMemberRecommendMethodTemp(usersVoList, user.getLabId(), user.getName(),
						recommendMethod.getName(), recommendAuditInfo.getId());
			});
		}
		return true;
	}

	private void receiveMemberRecommendMethodTemp(List<UsersVo> usersVoList, Long labId, String userName,
			String methodName, Long auditId) {
		for (UsersVo usersVo : usersVoList) {
			ReceiveMemberRecommendMethodTemp receiveMemberRecommendMethodTemp = new ReceiveMemberRecommendMethodTemp(
					labId, usersVo.getUserId(), usersVo.getLabMemberId(), userName, methodName, auditId);
			messageProducer.sendMessagePush(receiveMemberRecommendMethodTemp);
		}
	}

	/**
	 * @return com.brillilab.domain.vo.method.MethodRecommendInfo
	 * @Description: 获取推荐方法推荐信息
	 * @Params: [recommendId, user]
	 * @author wuzhiyu
	 * @date 2019/2/26 17:21
	 */
	public MethodRecommendInfo getRecommendInfo(Long recommendId, UserInfoVo user) {
		MethodRecommend methodRecommend = methodRecommendService.getRecommend(recommendId, user.getLabId());
		if (methodRecommend == null) {
			throw new BrillilabException(ResultEnum.DATA_NOT_EXIST);
		}
		MethodDetail methodDetail = methodLogic.getMethodDetail(methodRecommend.getMethodId(), null, user.getLabId());
		MethodRecommendInfo info = new MethodRecommendInfo();
		Lab lab = labService.selectById(methodRecommend.getLabId());
		info.setMethodDetail(methodDetail);
		info.setRecommendId(recommendId);
		info.setRecommendTime(methodRecommend.getRecommendTime());
		info.setLabName(lab.getName());
		MethodRecommendReceive receive = methodRecommendService
				.getReceiveByRecommendIdAndMemberId(methodRecommend.getId(), user.getLabMemberId());
		if (receive == null) {
			info.setStateStr(MethodDealStateEnum.WaiteDeal.getName());
			info.setState(MethodDealStateEnum.WaiteDeal.getState());
		} else if (receive.getState().equals(MethodDealStateEnum.Accept.getState())) {
			info.setStateStr(MethodDealStateEnum.Accept.getName());
			info.setState(MethodDealStateEnum.Accept.getState());
		} else {
			info.setStateStr(MethodDealStateEnum.Refuse.getName());
			info.setState(MethodDealStateEnum.Refuse.getState());
		}
		return info;
	}

	/**
	 * @return boolean
	 * @Description: 用户收到推荐后处理
	 * @Params: [deal, user]
	 * @author wuzhiyu
	 * @date 2019/2/26 17:22
	 */
	public Long recommendDeal(MethodRecommendDeal deal, UserInfoVo user) {
		Long myMethodId = null;
		MethodRecommend recommend = methodRecommendService.getRecommend(deal.getRecommendId(), user.getLabId());
		Assert.notNull(recommend, "数据不存在");
		Assert.isTrue(recommend.getLabId().equals(user.getLabId()), "数据不存在");
		if (deal.getState().equals(MethodDealStateEnum.Accept.getState())) {
			Method oriMethod = methodService.getById(recommend.getMethodId());
			MethodSourceEnum source = MethodSourceEnum.RECOMMEND;
			String newName = StringUtils.isEmpty(deal.getNewName()) ? null : deal.getNewName();
			myMethodId = dealToMyNewMethod(user, oriMethod, source, newName, recommend.getCreateTime(),
					user.getLabName(), null);
		}
		MethodRecommendReceive dbRecive = methodRecommendReceive
				.selectByLabMemberIdAndRecommendId(user.getLabMemberId(), deal.getRecommendId());
		if (dbRecive != null) {
			throw new BrillilabException(
					dbRecive.getState().equals(BoolEnum.TRUE.getValue()) ? "该推荐方法已接受" : "该推荐方法已拒绝");
		}
		MethodRecommendReceive receive = new MethodRecommendReceive();
		receive.setLabId(user.getLabId());
		receive.setLabMemberId(user.getLabMemberId());
		receive.setMethodId(recommend.getMethodId());
		receive.setState(deal.getState());
		receive.setMethodRecommendId(recommend.getId());
		methodRecommendService.saveReceive(receive);

		// fixme 附件处理 (只有接受时处理附件)
		if (myMethodId != null && deal.getState() == 1) {
			List<FileManage> fileList = fileManageService.selectList(OssFileEnum.Method, recommend.getMethodId());
			if (!CollectionUtils.isEmpty(fileList)) {
				List<FileManage> copies = new ArrayList<>();
				for (FileManage fileManage : fileList) {
					fileManage.setId(null);
					fileManage.setReferDataId(myMethodId);
					fileManage.setUserId(user.getId());
					fileManage.setLabId(user.getLabId());
					fileManage.setCreateTime(null);
					copies.add(fileManage);
				}
				fileManageService.insertBatch(copies);
			}
		}

		return myMethodId;
	}

	/**
	 * @return boolean
	 * @Description: 用户收到推荐后处理
	 */
	public boolean recommendDealBatch(List<MethodRecommendDeal> deals, UserInfoVo user) {

		Assert.isTrue(deals != null && deals.size() > 0, "deals can not be empty");

		List<Long> recommendIds = deals.stream().map(MethodRecommendDeal::getRecommendId).collect(Collectors.toList());
		List<MethodRecommend> recommends = methodRecommendService.getRecommendList(recommendIds, user.getLabId());
		List<Long> methodIds = recommends.stream().map(MethodRecommend::getMethodId).collect(Collectors.toList());
		List<Method> methods = methodService.selectListByIds(methodIds);

		List<RecommendMethodSync> syncs = new ArrayList<>();
		for (MethodRecommendDeal deal : deals) {
			MethodRecommend recommend = recommends.stream()
					.filter(e -> e.getId().equals(deal.getRecommendId()) && e.getLabId().equals(user.getLabId()))
					.findFirst().orElse(null);
			Assert.notNull(recommend, "数据不存在");
			Assert.isTrue(recommend.getLabId().equals(user.getLabId()), "数据不存在");
			if (deal.getState().equals(MethodDealStateEnum.Accept.getState())) {
				Method oriMethod = methods.stream().filter(e -> e.getId().equals(recommend.getMethodId())).findFirst()
						.orElse(null);
				if (oriMethod == null)
					continue;
				String newName = StringUtils.isEmpty(deal.getNewName()) ? null : deal.getNewName();
				RecommendMethodSync recommendMethodSync = new RecommendMethodSync();
				recommendMethodSync.setUserId(user.getId());
				recommendMethodSync.setLabId(user.getLabId());
				recommendMethodSync.setLabMemberId(user.getLabMemberId());
				recommendMethodSync.setLabName(user.getLabName());
				recommendMethodSync.setOriMethodId(oriMethod.getId());
				recommendMethodSync.setName(newName);
				recommendMethodSync.setRecommendTime(recommend.getCreateTime());
				recommendMethodSync.setRecommendId(deal.getRecommendId());
				syncs.add(recommendMethodSync);
			}
		}

		return recommendMethodSyncService.insertBatch(syncs) > 0;
	}

	/**
	 * @return void
	 * @Description: 处理成我的方法
	 * @Params: [user, oriMethod, source, newName]
	 * @author wuzhiyu
	 * @date 2019/2/26 17:22
	 */
	public Long dealToMyNewMethod(UserInfoVo user, Method oriMethod, MethodSourceEnum source, String newName, Date time,
			String sourceName, Long mySelectedTypeId) {
		MethodType myThird = methodLogic.getMyThirdTypeOrCreateNew(user, oriMethod, mySelectedTypeId);
		List<MethodPhase> sourcePhaseLIst = methodPhaseService.listByMethodId(oriMethod.getId(), null);
		List<MethodStep> sourceStepList = methodStepService.listByMethodId(oriMethod.getId(), null);
		List<MethodRelateReagent> sourceRelateReagentList = methodRelateReagentService
				.listMethodRelateReagentByMethodId(oriMethod.getId());
		List<MethodRelateTimer> sourceRelateTimerList = methodRelateTimerService.getByMethodId(oriMethod.getId());
		MethodFullInfo sourceInfo = methodLogic.composeMethodFullInfo(oriMethod, sourcePhaseLIst, sourceStepList,
				sourceRelateReagentList, sourceRelateTimerList);
		MethodSaveDto saveDto = new MethodSaveDto(null, sourceInfo, source, sourceName, time, true, newName, myThird,
				user, 0);
		Method myMethod = methodLogic.copyToMyMethod(saveDto);
		// 导入说明书
		List<FileManage> files = fileManageService.selectList(OssFileEnum.PublicMethod, oriMethod.getId());
		if (!CollectionUtils.isEmpty(files)){
			List<FileManage> newFiles = files.stream().map(e -> {
				e.setId(null);
				e.setReferDataId(myMethod.getId());
				e.setAttachType(OssFileEnum.Method.getKey());
				e.setLabId(user.getLabId());
				e.setUserId(user.getId());
				return e;
			}).collect(Collectors.toList());
			if (!CollectionUtils.isEmpty(newFiles)){
				fileManageService.insertBatch(newFiles);
			}
		}
		return myMethod.getId();
	}

	/**
	 * @return com.brillilab.common.entity.PageVo<com.brillilab.domain.vo.method.MethodRecommendListItem>
	 * @Description: 我的方法推荐历史
	 * @Params: [user, pageIndex, rows]
	 * @author wuzhiyu
	 * @date 2019/2/26 17:22
	 */
	public PageVo<MethodRecommendListItem> listMyRecommend(UserInfoVo user, Integer pageIndex, Integer rows,
			Integer state) {
		PageVo<MethodRecommendAudit> page = methodRecommendAuditService
				.listPagedByRecommendMemberId(user.getLabMemberId(), pageIndex, rows, state);
		PageVo<MethodRecommendListItem> result = new PageVo<>();
		BeanUtils.copyProperties(page, result);
		List<MethodRecommendListItem> content = new ArrayList<>();
		result.setContent(content);
		List<LabMemberVo> memList = labMemberService.selectByLabId(user.getLabId());
		if (!CollectionUtils.isEmpty(page.getContent())) {
			List<Long> methodIdList = page.getContent().stream().map(MethodRecommendAudit::getMethodId).distinct()
					.collect(Collectors.toList());
			List<Method> methodList = methodService.listByIdList(methodIdList, null);
			List<Long> typeIdList = methodList.stream().map(Method::getMethodTypeId).distinct()
					.collect(Collectors.toList());
			List<MethodType> typeList = methodTypeService.listByIdList(typeIdList);
			page.getContent().forEach(s -> {
				MethodRecommendListItem item = new MethodRecommendListItem();
				item.setId(s.getId());
				Method method = methodList.stream().filter(m -> m.getId().equals(s.getMethodId())).findFirst().get();
				item.setMethodName(method.getName());
				item.setRecommendTime(s.getCreateTime());
				item.setTypeName(typeList.stream().filter(t -> t.getId().equals(method.getMethodTypeId())).findFirst()
						.get().getName());
				if (s.getState().equals(RecommendAuditStateEnum.AuditWait.getState())) {
					item.setStateStr(RecommendAuditStateEnum.AuditWait.getName());
				} else if (s.getState().equals(RecommendAuditStateEnum.Recommend.getState())) {
					item.setStateStr(RecommendAuditStateEnum.Recommend.getName());
					item.setAuditorName(memList.stream().filter(m -> m.getLabMemberId().equals(s.getAuditorId()))
							.findFirst().get().getUserName());
				} else if (s.getState().equals(RecommendAuditStateEnum.AuditRefuse.getState())) {
					item.setStateStr(RecommendAuditStateEnum.AuditRefuse.getName());
					item.setAuditorName(memList.stream().filter(m -> m.getLabMemberId().equals(s.getAuditorId()))
							.findFirst().get().getUserName());
				} else {
					item.setStateStr(RecommendAuditStateEnum.RecommendCancel.getName());
					item.setAuditorName(memList.stream().filter(m -> m.getLabMemberId().equals(s.getAuditorId()))
							.findFirst().get().getUserName());
				}
				item.setAuditOpinon(s.getAuditOpinion());
				item.setAuditTime(s.getAuditTime());
				item.setState(s.getState());
				item.setReason(s.getReason());
				item.setFirstTypeId(s.getFirstMethodTypeId());
				content.add(item);
			});
		}
		return result;
	}

	/**
	 * @return com.brillilab.domain.vo.method.MethodRecommendAuditResult
	 * @Description: 获取推荐结果
	 * @Params: [user, auditId]
	 * @author wuzhiyu
	 * @date 2019/2/26 17:22
	 */
	public MethodRecommendAuditResult getAuditResult(UserInfoVo user, Long auditId) {
		MethodRecommendAudit audit = methodRecommendAuditService.getById(user.getLabMemberId(), auditId);
		Assert.notNull(audit, "数据不存在");
		LabMemberVo mem = labMemberService.selectMemberVo(audit.getAuditorId());
		MethodRecommendAuditResult result = new MethodRecommendAuditResult();
		result.setAuditorName(mem.getUserName());
		result.setAuditTime(audit.getAuditTime());
		result.setAuditOpinion(audit.getAuditOpinion());
		if (audit.getState().equals(RecommendAuditStateEnum.AuditWait.getState())) {
			result.setStateStr(RecommendAuditStateEnum.AuditWait.getName());
		} else if (audit.getState().equals(RecommendAuditStateEnum.Recommend.getState())) {
			result.setStateStr(RecommendAuditStateEnum.Recommend.getName());
		} else if (audit.getState().equals(RecommendAuditStateEnum.AuditRefuse.getState())) {
			result.setStateStr(RecommendAuditStateEnum.AuditRefuse.getName());
		} else {
			result.setStateStr(RecommendAuditStateEnum.RecommendCancel.getName());
		}
		return result;
	}

	// 管理员审核通过后保存成自己的方法
	public Long auditorSaveToRecommendMethod(NewMethod newMethod, UserInfoVo user) {
		MethodType myThird = methodLogic.findMyThirdTypeOrCreateNew(user.getId(), newMethod.getMethodTypeId(), true,
				true);
		Method method = new Method();
		BeanUtils.copyProperties(newMethod, method);
		method.setMethodTypeId(myThird.getId());
		method.setFromTime(newMethod.getFromTime() != null ? newMethod.getFromTime() : new Date());
		method.setFromType(MethodSourceEnum.RECOMMEND.getSourceType());
		method.setSourceName(user.getLabName());
		method.setImportTime(new Date());
		methodLogic.setVersionUserIdCurrentDeleteAndSave(newMethod, user, method, 0, true);
		methodLogic.methodAttachFileDeal(newMethod.getUploadFileIdList(), newMethod.getCopyFileIdList(), user,
				method.getId());
		methodLogic.methodLabViewAndTypeUsage(user, myThird, method);
		// 更新推荐方法ID
		method.setRecommendMethodId(method.getId());
		methodService.updateById(method);
		return method.getId();
	}

	/**
	 * @description 执行同步（他人不要用）
	 * @author WuMenghao
	 * @data 2019/07/17
	 */
	public void executeMethodSync(RecommendMethodSync sync, UserInfoVo user, Method oriMethod, MethodSourceEnum source,
			MethodRecommendReceive receive) {
		final String lockKey = "METHOD_SYNC_ID_LOCK";

		if (lock(lockKey)) {
			this.dealToMyNewMethod(user, oriMethod, source, sync.getName(), sync.getRecommendTime(), sync.getLabName(),
					null);
			methodRecommendService.saveReceive(receive);
			recommendMethodSyncService.update(sync);
			unlock(lockKey);
		}

	}

	private void unlock(String lockKey) {
		if (redisCache.getObj(lockKey) == null) {
			throw new BrillilabException(ResultEnum.LOCK_NOT_RELEASE);
		}
		redisCache.delete(lockKey);
	}

	private boolean lock(String lockKey) {
		if (redisCache.getObj(lockKey) != null) {
			return false;
		}
		redisCache.set(lockKey, true);
		return true;
	}
}
